package com.coco.bargain.service.impl;

import cn.hutool.core.lang.Snowflake;
import com.coco.bargain.pojo.TbBargainGoods;
import com.coco.bargain.pojo.TbBargainItem;
import com.coco.bargain.pojo.TbBargainUser;
import com.coco.bargain.pojo.TbBargainUserHelp;
import com.coco.bargain.service.BargainGoodsService;
import com.coco.bargain.service.BargainItemService;
import com.coco.bargain.service.BargainService;
import com.coco.bargain.service.BargainUserService;
import com.coco.bargain.util.RandomBargainAmountUtil;
import com.coco.bargain.util.TypeBargainCount;
import com.coco.common.constant.Constant;
import com.coco.common.constant.TYPE;
import com.coco.common.exception.CustomException;
import com.coco.common.exception.ErrorEnum;
import com.coco.common.util.RandomString;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * @Author: guofeng
 * @Date: 2021/07/22/16:03
 * @Description:
 */
@Slf4j
@Service
public class BargainServiceImpl implements BargainService {


    public static Object object = new Object();
    private static final Snowflake snowflake = new Snowflake(1, 1);
    private static final HashMap<String, Long> CunBargainList = new HashMap<>(16);
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private Redisson redisson;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private BargainGoodsService bargainGoodsService;

    @Autowired
    private BargainUserService bargainUserService;

    @Autowired
    private BargainItemService bargainItemService;

    public <T> T hGetList(String key, String field, Class<T> obj) {
        Object value = redisTemplate.opsForHash().get(key, field);
        System.out.println(value);
        return (T) value;
    }

    @Override
    public String bargainAdd(Long skuId) {


        //查询当前商品sku信息
        TbBargainItem skuGood = bargainItemService.findById(skuId);

        RLock lock = redisson.getLock(skuGood.getId().toString());
        try {
            lock.lock();
            //查询当前商品信息
            TbBargainGoods good = bargainGoodsService.findById(skuGood.getBargainGoodsId());
            if (good.getStartTime().getTime() > System.currentTimeMillis()) {
                return Constant.BargainConstant.NO_CREATE_TIME;
            } else if (good.getEndTime().getTime() < System.currentTimeMillis()) {
                return Constant.BargainConstant.NO_ENT_TIME;
            } else {
                //当前商品还有库存 可以进行砍价
                if (skuGood.getResidueNum() > 0) {
                    //查询当前用户对于当前商品砍了几次
                    Integer userAlreadyBargainCount = bargainUserService.findByUserBargainGoodCount(skuGood.getBargainGoodsId());
                    if (userAlreadyBargainCount == null) {
                        userAlreadyBargainCount = 0;
                    }
                    //查询此商品能砍几次
                    if (userAlreadyBargainCount < good.getShopNum()) {
                        //生成砍价
                        String randomCard = RandomString.getRandom(24, TYPE.CAPITAL);
                        //添加到redis
                        List<String> random = RandomBargainAmountUtil.random(skuGood.getBargainPrice().intValue(), good.getPeopleNum(), 0.9, 0.95, good.getMinPrice().intValue());
                        random.forEach(money -> {
                            redisTemplate.opsForList().rightPush(randomCard, money);
                        });
                        //设置redis过期时间
                        redisTemplate.expire(randomCard, 1, TimeUnit.DAYS);
                        //判断当前商品有没有去砍
                        Set<Object> yaoq = redisTemplate.opsForHash().keys(Constant.BargainConstant.STORY_MAP);
                        Boolean flag = true;
                        for (Object o : yaoq) {
                            String v = (String) o;
                            if (v.equals(good.getId().toString())) {
                                flag = false;
                            }
                        }
                        //如果没有人去砍就新增一个
                        if (flag == true) {
                            redisTemplate.opsForHash().put(Constant.BargainConstant.STORY_MAP, good.getId().toString(), new TypeBargainCount(good.getBargainNum(), null));
                        }
                        //加入时间集合, 定时器定时判断
                        CunBargainList.put(randomCard, skuGood.getId());

                        //修改商品信息
                        good.setResidueNum(good.getResidueNum() - 1);
                        good.setJoinNum(good.getJoinNum() + 1);
                        bargainGoodsService.updateById(good);

                        //修改sku商品信息
                        skuGood.setResidueNum(skuGood.getResidueNum() - 1);
                        bargainItemService.update(skuGood);
                        //mq添加一条砍价记录
                        rabbitTemplate.convertAndSend(Constant.BargainConstantMQ.bargainRecordAdd,
                                new TbBargainUser(snowflake.nextId(), randomCard, 1L, good.getId(), new BigDecimal(0), 0, new Date(), userAlreadyBargainCount + 1, good.getBargainNum() - userAlreadyBargainCount - 1, good.getEndTime(), null, 0));
                        return randomCard;
                    }
                }
            }
        } finally {
            lock.unlock();
        }
        return Constant.BargainConstant.NO_BARGAIN;
    }


    @Override
    public String firstBargain(String card) {
        Object money = redisTemplate.opsForList().leftPop(card);
        TbBargainUser user = bargainUserService.findByCard(card);
        user.setMyBargainPrice(new BigDecimal(money.toString()));
        user.setStatus(1);
        rabbitTemplate.convertAndSend(Constant.BargainConstantMQ.userFirstBargain, user);
        return money.toString();
    }


    @Override
    public String helpMe(Integer helpUid, String card) {
        //判单uid和gid是否为空
        if (helpUid == null || card == null) {
            throw new CustomException(ErrorEnum.BARGAIN_HELP_USER);
        }
        Long gid = bargainUserService.findByCard(card).getBargainItemId();

        String lockKey = Constant.BargainConstant.LOCK_KEY + card;
        String userKey = Constant.BargainConstant.USER_KEY + card;
        String str = null;
        //设置分布式锁
        RLock lock = redisson.getLock(lockKey);
        try {
            //分布式锁 上锁
            lock.lock();
            TypeBargainCount typeBargainCount = this.hGetList(Constant.BargainConstant.STORY_MAP, gid.toString(), TypeBargainCount.class);

            boolean flag = true;
            if (typeBargainCount.getMap() != null) {
                HashMap<Integer, Integer> personCount = typeBargainCount.getMap();
                for (Map.Entry<Integer, Integer> entry : personCount.entrySet()) {
                    if (entry.getKey().equals(helpUid)) {
                        flag = false;
                        if (entry.getValue() >= typeBargainCount.getCount()) {
                            throw new CustomException(ErrorEnum.BARGAIN_ALREADY_HELP_USER);
                        } else {
                            //修改用户此类型商品砍过的次数
                            entry.setValue(entry.getValue() + 1);
                        }
                    }
                }
            } else {
                typeBargainCount.setMap(new HashMap<>());
                //用户第一次砍这个商品
                typeBargainCount.getMap().put(helpUid, 1);
                flag = false;
            }
            if (flag == true) {
                //用户第一次砍这个商品
                typeBargainCount.getMap().put(helpUid, 1);
            }

            Object money = redisTemplate.opsForList().leftPop(card);
            if (money != null) {
                if (!redisTemplate.opsForSet().isMember(userKey, helpUid)) {
                    str = money.toString();
                    //将砍价成功的用户加入集合
                    redisTemplate.opsForSet().add(userKey, helpUid);
                    redisTemplate.opsForHash().put(Constant.BargainConstant.STORY_MAP, gid.toString(), typeBargainCount);
                    //添加帮砍记录
                    TbBargainUser byCard = bargainUserService.findByCard(card);
                    rabbitTemplate.convertAndSend(Constant.BargainConstantMQ.helpMeBargainUserAdd, new TbBargainUserHelp(null, helpUid.longValue(), gid, byCard.getId(), new BigDecimal(money.toString()), new Date(), null, 0));
                    if (redisTemplate.opsForList().size(card) == 0) {
                        CunBargainList.remove(card);
                        rabbitTemplate.convertAndSend(Constant.BargainConstantMQ.bargainOk, gid);
                        rabbitTemplate.convertAndSend(Constant.BargainConstantMQ.bargainUserOk, card);
                    }
                } else {
                    redisTemplate.opsForList().leftPushAll(card, money);
                    throw new CustomException(ErrorEnum.BARGAIN_ALREADY_HELP_USER);
                }
            } else {
                throw new CustomException(ErrorEnum.BARGAIN_CONNECTION_TIME_OUTS);
            }
        } finally {
            //解锁
            lock.unlock();
        }
        return str;
    }


    @Scheduled(cron = "0/2 * * * * *")
    public void timer() {
        for (Map.Entry<String, Long> entry : CunBargainList.entrySet()) {
            Long expire = redisTemplate.opsForValue().getOperations().getExpire(entry.getKey());
            log.info("当前还剩下{}秒", expire);
            if (expire == -2) {
                //商品回调加1
                rabbitTemplate.convertAndSend(Constant.BargainConstantMQ.bargainShopRollback, entry.getValue());
                CunBargainList.remove(entry.getKey());
            }
        }
    }

}
